#pragma once

#include <iostream>
#include <vector>
#include <queue>
#include <pthread.h>
#include <unistd.h>
#include "Thread.hpp"

using namespace ThreadModule;

const static int gdefaultthreadnum = 3;


template<typename T>
class ThreadPool
{
private:
    void LockQueue()
    {
        pthread_mutex_lock(&_mutex);
    }
    void UnlockQueue()
    {
        pthread_mutex_unlock(&_mutex);
    }
    void ThreadSleep()
    {
        pthread_cond_wait(&_cond,&_mutex);
    }
    void ThreadWakeup()
    {
        pthread_cond_signal(&_cond);
    }
    void ThreadWakeupAll()
    {
        pthread_cond_broadcast(&_cond);
    }
public:
    ThreadPool(int threadnum = gdefaultthreadnum)
        :_threadnum(threadnum), _waitnum(0), _isrunning(false)
    {
        pthread_mutex_init(&_mutex,nullptr);
        pthread_cond_init(&_cond,nullptr);
    }
    void HandlerTask(std::string name)
    {   
        while(true)
        {
            LockQueue();
            while(_task_queue.empty() && _isrunning)
            {
                _waitnum++;
                ThreadSleep();
                _waitnum--;
            }
            if(_task_queue.empty() && !_isrunning)
            {
                UnlockQueue();
                std::cout << name << "quit.." << std::endl;
                sleep(1);
                break;
            }
            T t = _task_queue.front();
            _task_queue.pop();
            UnlockQueue();

            // t();
        }
    }
    void InitThreadPool()
    {
        for(int num = 0;num < _threadnum;num++)
        {
            std::string name = "thread-" + std::to_string(num + 1);
            _threads.emplace_back(std::bind(&ThreadPool::HandlerTask,this,std::placeholders::_1),name);
        }
        _isrunning = true;
    }
    void Start()
    {
        for(auto &thread : _threads)
        {
            thread.Start();
        }
    }
    void Stop()
    {
        LockQueue();
        _isrunning = false;
        ThreadWakeupAll();
        UnlockQueue();
    }
    void Wait()
    {
        for(auto &thread : _threads)
        {
            thread.Join();
        }
    }
    bool Enqueue(const T &t)
    {
        bool ret = false;
        LockQueue();
        if(_isrunning)
        {
            _task_queue.push(t);
            if(_waitnum > 0)
            {
                ThreadWakeup();
            }
            ret = true;
        }
        UnlockQueue();
        return ret;
    }
    ~ThreadPool()
    {
        pthread_mutex_destroy(&_mutex);
        pthread_cond_destroy(&_cond);
    }
private:
    int _threadnum;
    std::vector<Thread> _threads;
    std::queue<T> _task_queue;
    pthread_mutex_t _mutex;
    pthread_cond_t _cond;

    int _waitnum;
    bool _isrunning;
};
